package framework.spatial;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.LinkedList;

/**
 *
 * @author Lukáš Marek
 */
public class FRDSpatialGLine extends FRDSpatialGItem
{
	LinkedList<Point> points;
	boolean add = false;
	
	public FRDSpatialGLine()
	{
		points = new LinkedList<>();
		defaultColor = new Color(255, 0, 0);
		SelectedColor = new Color(0, 0, 255);
	}

	public void setLocation(LinkedList<Point> points)
	{
		for (int i = 0; i < points.size(); i++)
		{
			this.points.add(new Point(points.get(i)));
		}
	}
	
	public LinkedList<Point> getLocation()
	{
		return points;
	}
	
	@Override
	public void setPoints(Point p1, Point p2)
	{
		if (points.size() < 1) 
		{
			if (p1.distance(p2) > 3)
			{
				points.add(new Point(mapPoint(p1)));
				points.add(new Point(mapPoint(p2)));
			}
		}
		else if (add)
		{
			points.add(new Point(mapPoint(p2)));
			add = false;
		}
		else
			points.getLast().setLocation(mapPoint(p2));
	}
	
	@Override
	public boolean addPoints(Point p1, Point p2)
	{
		if (points.size() > 1)
		{
			points.getLast().setLocation(mapPoint(p2));
			add = true;
		}
		return false;
	}

	@Override
	public void draw(Graphics g)
	{
		if (selected)
			g.setColor(SelectedColor);
		else
			g.setColor(defaultColor);

		for (int i=0; i < (points.size() - 1); i++)
		{
			Point r1 = showPoint(points.get(i));
			Point r2 = showPoint(points.get(i + 1));
			g.drawLine(r1.x, r1.y, r2.x, r2.y);
		}
	}

	@Override
	public void drawEdit(Graphics g, Point pt1, Point pt2)
	{
		g.setColor(editColor);
		int c = -1;
		Point diff = new Point(0,0);
		for (int i=0; i < points.size(); i++)
		{
			if (near(points.get(i), mapPoint(pt1)))
			{
				c = i;
				break;
			}
		}
		if (c == 0)
		{
			Point r1 = pt2;
			Point r2 = showPoint(points.get(c + 1));
			g.drawLine(r1.x, r1.y, r2.x, r2.y);
		}
		else if (c == points.size() - 1)
		{
			Point r1 = showPoint(points.get(c - 1));
			Point r2 = pt2;
			g.drawLine(r1.x, r1.y, r2.x, r2.y);
		}
		else if (c > 0)
		{
			Point r1 = showPoint(points.get(c - 1));
			Point r2 = pt2;
			Point r3 = showPoint(points.get(c + 1));
			g.drawLine(r1.x, r1.y, r2.x, r2.y);
			g.drawLine(r2.x, r2.y, r3.x, r3.y);
		}
		else if (undermouse(pt1))
		{
			diff.setLocation(pt2.x - pt1.x,pt2.y - pt1.y);
			for (int i=0; i < (points.size() - 1); i++)
			{
				Point r1 = showPoint(points.get(i));
				Point r2 = showPoint(points.get(i + 1));
				g.drawLine(r1.x + diff.x, r1.y + diff.y, r2.x + diff.x, r2.y + diff.y);
			}
		}
	}
	
	@Override
	public void saveEdit(Point pt1, Point pt2)
	{
		int c = -1;
		for (int i=0; i < points.size(); i++)
		{
			if (near(points.get(i), mapPoint(pt1)))
			{
				c = i;
				break;
			}
		}
		if (c >= 0)
		{
			points.get(c).setLocation(mapPoint(pt2));
		}
		else if (undermouse(pt1))
		{
			Point mp1 = mapPoint(pt1);
			Point mp2 = mapPoint(pt2);
			for (int i=0; i < points.size(); i++)
			{
				points.get(i).x += mp2.x - mp1.x;
				points.get(i).y += mp2.y - mp1.y;
			}
		}
	}

	@Override
	public boolean undermouse(Point _p)
	{
		Point p = mapPoint(_p);
		for (int i=0; i < (points.size() - 1); i++)
		{
			Point r1 = (Point)points.get(i);
			Point r2 = (Point)points.get(i + 1);
			if (Math.max(Math.abs(r1.x - p.x),Math.abs(r2.x - p.x)) > (Math.abs(r1.x - r2.x) + 15)) continue;
			if (Math.max(Math.abs(r1.y - p.y),Math.abs(r2.y - p.y)) > (Math.abs(r1.y - r2.y) + 15)) continue;
			int a = r2.y - r1.y;
			int b = r1.x - r2.x;
			int c = -1*(r1.x*a + r1.y*b);
			if ((Math.abs(a*p.x + b*p.y + c)/((int)(Math.sqrt(a*a + b*b)))) < 15) return true;
		}
		return false;
	}
	
	/**
	 * Zjisteni blizkosti dvou bodu
	 * @param _p prvni bod
	 * @param p druhy bod
	 * @return true pokud sou body blizko
	 */
	protected boolean near(Point _p, Point p)
	{
		double zoom = back.getZoom();
		return (p.distance(_p) < (5*4/zoom));
	}
	
	/**
	 * prepocita bod z mapy na bod v panelu
	 * @param pt bod
	 * @return 
	 */
	private Point showPoint(Point pt)
	{
		double scale = back.getScale();
		Point zoomLoc = back.getPosition();
		double zoom = back.getZoom();
		double df = scale / zoom;
		Point ret = new Point();
		ret.x = (int)((pt.x - zoomLoc.x) / df);
		ret.y = (int)((pt.y - zoomLoc.y) / df);
		return ret;
	}
	
	/**
	 * prepocita bod z panelu na bod v mape
	 * @param pt
	 * @return 
	 */
	private Point mapPoint(Point pt)
	{
		double scale = back.getScale();
		Point zoomLoc = back.getPosition();
		double zoom = back.getZoom();
		double df = scale / zoom;
		Point ret = new Point();
		ret.x = (int)(pt.x * df) + zoomLoc.x;
		ret.y = (int)(pt.y * df) + zoomLoc.y;
		return ret;
	}
}
